#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h> 	/* close() */

#include <signal.h>
#include <sys/wait.h>

#include <sys/shm.h> 	// Contiene las definiciones de memoria compartida.
#include <sys/ipc.h>	// ftok

#include <arpa/inet.h>
#include <errno.h>
 
#include <pthread.h>

#define BACKLOG 5
	
typedef struct {
	pid_t pid;
	char buffer[1000];
	int nro_jugador;
} t_mensaje;

typedef struct {
	int jugador1;
	int jugador2;
	pid_t pid_servidor_partido;
} t_partido;

int PUERTO=45559;
int MARCA=1;
int socket_escucha, socket_comunicacion;

void esperar_hijo(int sig);
void finalizar_programa(int sig);

void* comunicacion_cliente(void*);

pthread_t hiloConexion, hiloResumen, hiloWait;
pthread_attr_t atributos;

int main(void)
{		
	//t_mensaje message;

	signal(SIGCHLD, esperar_hijo);	/* manejos de zombies */
	signal(SIGINT, finalizar_programa);		/* manejo ctrl+c */
	signal(SIGALRM, finalizar_programa);		/* manejo ctrl+c */
	
	/** Creo un socket de escucha. */
	if((socket_escucha = socket(AF_INET, SOCK_STREAM, 0))==-1) {
		printf("Error al crear el socket.\n");
		exit(1);
	} else {
		printf("Se creo el socket correctamente\n");
	}
	
	/** Creo una estructura para indicar por que red y puerto voy a escuchar los pedidos. */
	struct sockaddr_in listen_address;		//Dirreccion de escucha.
	struct sockaddr_in caller_address;		//Dirreccion de escucha.
	
	listen_address.sin_family 		= AF_INET;  
	listen_address.sin_port 		= htons(PUERTO);  			// Número de puerto donde recibirá paquetes el programa  
	listen_address.sin_addr.s_addr  = inet_addr ("10.0.2.15");	// IP por la que recibirá paquetes el programa  
	
	/** Asociar direccion ip y puerto al socket */
	if((bind(socket_escucha, (struct sockaddr *)&listen_address, sizeof(struct sockaddr)))==-1) {
		printf("Error al asociar direccion ip y puerto al socket.\n");
		exit(1);
	}
	
	/** Habilitar el socket para recibir conexiones en forma pasiva. */
	if((listen(socket_escucha,BACKLOG))==-1) {
		printf("Error al Habilitar el socket para recibir conexiones en forma pasiva.\n");
		exit(1);
	} else {
		printf("Se habilita al socket para recibir conexiones en forma pasiva... El socket se encuentra escuchando....\n");
	}
	
	/** Se programa un alarma para terminar tiempo de conexion del servidor con el cliente.*/
	alarm(20);
	
	int size_caller_address;
	int cont = 0;
	
	int socket_comunicacion[100];
	
	/** Esta parte del codigo se encarga de encolar a todas las conexiones en socket_comunicacion[] dentro de un tiempo. */
	while(MARCA){
		size_caller_address=sizeof(struct sockaddr);
		
		/** Se crea una conexion con el cliente.*/
		if((socket_comunicacion[cont]=accept(socket_escucha,(struct sockaddr *)&caller_address,(socklen_t *)&size_caller_address))==-1) {
			//printf("Error al recibir una conexión.\n");
			continue;
		} else {
			printf("El servidor se conecto a un cliente... \n");
			cont++;
		}
		
	}
	
	MARCA=1;

	///CREO LOS SERVIDORES DE PARTIDA.
	int partidas=cont/2;
	
	int i, k;
	
	pid_t hijo;
	
	for (i = 0; i < partidas; i++) {

		hijo = fork();

		switch (hijo) 
		{
			case -1://error
				printf("\nError: no se pudo crear proceso\n");
				exit(1);
				break;

			//*Servidores de partida.****************************************************************
			case 0:
				
				/** Cierro el socket de escucha. */
				close(socket_escucha);
	
				pthread_t thread_id[2];
					
				while(MARCA)
				{
					pthread_create( &thread_id[0], NULL, comunicacion_cliente,(void*)socket_comunicacion[0]);
					pthread_create( &thread_id[1], NULL, comunicacion_cliente,(void*)socket_comunicacion[1]);
		
				}
				
				for(k=0; k < 2; k++) {
					pthread_join( thread_id[k], NULL);
				}
				
				break;
		}
	}
	
	for (i = 0; i < cont; i++)
		wait(NULL);
	
	printf("Se acabo el tiempo\n");
	
	for(i=0;i<100;i++)
		close(socket_comunicacion[i]);
	
	/** Cierro el socket de escucha. */
	close(socket_escucha);
	
    
    return 0;
}

void* comunicacion_cliente(void* identificador_socket)
{
	t_mensaje message;
	int socket_comunicacion=(int)identificador_socket;
	
	strcpy(message.buffer,"Ya te encuentraas conectado con otro cliente......\n");
	message.pid=getpid();
	
	send(socket_comunicacion, &message, sizeof(message), 0);
	
	return NULL;
}

void esperar_hijo(int sig)
{
    int estado;
    while (wait3(&estado, WNOHANG, (struct rusage *)0) >= 0);
}

/* Caida del servidor ctrl+c*/
void finalizar_programa(int sig)
{
	MARCA=0;
	close(socket_comunicacion);
	close(socket_escucha);
	printf("\nServidor desconectado.\n");
}
